--[[
	Broker_CPU, display CPU/Memory usage, Framerate, Latency for World of Warcraft
	Copyright (C) 2010 Arnaud Dovi (ad@heapoverflow.com)

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
]]

-- At this point the garbage of the mod is sometimes at 140KB
-- I clean it so I can be sure the mod's garbage is at 0KB
-- This ensure that the memory usage reported belonging to
-- BrokerCPU are only generated by it and not the mod + a
-- game client or library caveat.
if collectgarbage then collectgarbage("collect") end

if not LibStub then error("Broker_CPU requires LibStub") end
local obj, QTip, minimap, dewdrop, event, first, paused, lastactive, dewoptions
local lastotalcpu, lastotalmem, lastlat, lastlatb, scriptProfile, db, mb, kb, fps_
local icon                           = "Interface\\AddOns\\Broker_CPU\\icon"
local arrowa                         = "Interface\\AddOns\\Broker_CPU\\arrowa"
local arrowb                         = "Interface\\AddOns\\Broker_CPU\\arrowb"
local arrowc                         = "Interface\\AddOns\\Broker_CPU\\arrowc"
local MINOR_VERSION                  = tonumber(("$Revision: 2 $"):match("%d+"))
local L, mods, oldmem, oldcpu, tlist = {}, {}, {}, {}, {}

local _G                        = getfenv(0)
local CreateFrame               = _G.CreateFrame
local IsAddOnLoaded             = _G.IsAddOnLoaded
local GetAddOnInfo              = _G.GetAddOnInfo
local GetAddOnCPUUsage          = _G.GetAddOnCPUUsage
local GetAddOnMemoryUsage       = _G.GetAddOnMemoryUsage
local UpdateAddOnCPUUsage       = _G.UpdateAddOnCPUUsage
local UpdateAddOnMemoryUsage    = _G.UpdateAddOnMemoryUsage
local GetFramerate, GetNetStats = _G.GetFramerate, _G.GetNetStats
local GetText, wipe             = _G.GetText, _G.wipe

local string_format  = _G.string.format
local string_find    = _G.string.find
local string_len     = _G.string.len
local string_sub     = _G.string.sub
local string_gsub    = _G.string.gsub
local string_lower   = _G.string.lower
local table_sort     = _G.table.sort
local table_concat   = _G.table.concat
local tonumber       = _G.tonumber
local print          = _G.print
local type           = _G.type
local pairs          = _G.pairs
local math_modf      = _G.math.modf
local collectgarbage = _G.collectgarbage

-- Eventual localization changes goes here
local function OnInitializeLocalization()
	if GetLocale() == "frFR" then fps_ = "ips" mb = "Mo" kb = "Ko" else fps_ = "fps" mb = "MB" kb = "KB" end
	L["N_TITLE"]       = "BrokerCPU"
	L["N_MEM"]         = "Memory"
	L["N_MAP"]         = "Minimap"
	L["N_ICO"]         = "Icons"
	L["N_MOD"]         = "AddOns"
	L["N_THR"]         = "Thresholds"
	L["N_thr"]         = "threshold"
	L["N_THR_LDB"]     = "Broker text"
	L["N_THR_TIP"]     = "Tooltip text"
	L["N_CPU"]         = "CPU Profiling"
	L["N_DET"]         = "Detach"
	L["N_FPS"]         = "Framerate"
	L["N_LAT"]         = "Latency"
	L["N_GAR"]         = "Garbage"
	L["N_SC"]          = "Scale"
	L["N_HID"]         = "Hide"
	L["N_TOT"]         = "Total"
	L["N_BLI"]         = "Total & Blizzard"
	L["N_LIB"]         = "Libraries"
	L["N_L_QTIP"]      = "LibQTip"
	L["N_L_ICO"]       = "LibDBIcon"
	L["N_L_DEW"]       = "DewdropLib"
	L["N_DAT_TIP"]     = "Tooltips"
	L["N_DAT_LDB"]     = "LDB display"
	L["Q_NAME"]        = "BrokerCPUQTip"
	L["SLASH_SEP"]     = " - "
	L["COLOR_GOLD"]    = "|cfffed100"
	L["COLOR_ACE3"]    = "|cff33ff99"
	L["COLOR_GREY"]    = "|cff808080"
	L["COLOR_RED"]     = "|cffff0000"
	L["COLOR_GREEN"]   = "|cff33ff33"
	L["COLOR_HINT1"]   = "|cffeda55f"
	L["COLOR_ADDON"]   = "|cff00ffff"
	L["COLOR_CPU"]     = "|cff0095dd"
	L["COLOR_SUFF"]    = L["COLOR_GOLD"]
	L["COLOR_MEM"]     = L["COLOR_CPU"]
	L["COLOR_STOP"]    = "|r"
	L["LINE_STOP"]     = "\n"
	L["TOGGLE_CPU"]    = "Toggle the client CPU profiling."
	L["TOGGLE_MEM"]    = "Toggle memory collection in %s."
	L["TOGGLE_FPS"]    = "Toggle framerate collection in %s."
	L["TOGGLE_LAT"]    = "Toggle latency collection in %s."
	L["TOGGLE_MAP"]    = "Toggle minimap icon."
	L["TOGGLE_ICO"]    = "Toggle the arrow icons."
	L["TOGGLE_KLL"]    = "Set a maximum number of AddOns to show."
	L["TOGGLE_LDB"]    = "Threshold (in seconds) of the broker text update."
	L["TOGGLE_TIP"]    = "Threshold (in seconds) of the tooltip update."
	L["TOGGLE_DET"]    = "Toggle detached tooltip."
	L["TOGGLE_RST"]    = "Reset the AddOn to default settings."
	L["TOGGLE_HID"]    = "Hide a detached QTip. (or Shift+Click the Broker icon)"
	L["TOGGLE_GAR"]    = "Force the client to collect the garbage memory"
	L["TOGGLE_L_QTIP"] = "Toggle between the use of LibQTip or GameTooltip."
	L["TOGGLE_L_ICO"]  = "Toggle the use of LibDBIcon."
	L["TOGGLE_L_DEW"]  = "Toggle the use of DewdropLib."
	L["TOGGLE_SC"]     = "Change the qtip scale."
	L["NOT_QTIP"]      = "(requires LibQTip-1.0)"
	L["NOT_DBICO"]     = "(requires LibDBIcon-1.0)"
	L["NOT_DEW"]       = "(requires DewdropLib)"
	L["TIPHINT1"]      = "%sFor cpu usage, the mod requires%scpu profiling enabled.%s"
	L["TIPHINT2"]      = "%sRemember to switch the profiling off%swhen you don't need it.%s"
	L["TIPHINT3"]      = "%sFor memory usage, just enable the%soption.%s"
	L["TIPHINT4"]      = "%sCtrl-Alt-LClick%s %sto enable/disable CPU%sprofiling.%s"
	L["TIPHINT5"]      = "%sType%s %s/brokercpu%s %sfor more usages.%s"
	L["EXPANDED"]      = "The tooltip goes out of screen, autoresizing to show 5 AddOns."
	--[[
	-- In the case you want to submit a new localization
	-- you can enclose it in a if chain like below
	if GetLocale() == "frFR" then
		L["N_TITLE"] = "BrokerCPU"
		-- etc...
	end
	]]
end

local function formatTime(n)
	if n > 1000 then
		return string_format("%.1f", n/1000), "s"
	end
	return string_format("%.1f", n), "ms"
end

local function formatMemory(n, ldbtext)
	local pattern
	if not ldbtext then pattern = "%.2f" else pattern = "%.1f" end
	if n < 1024 then
		return string_format(pattern, n), kb
	end
	return string_format(pattern, n / 1024), mb
end

-- Tekkub's ColorGradient() http://www.wowwiki.com/ColorGradient
local function colorGradient(perc, r1, g1, b1, r2, g2, b2, r3, g3, b3)
	if perc >= 1 then return r3, g3, b3 elseif perc <= 0 then return r1, g1, b1 end
	local segment, relperc = math_modf(perc*2)
	if segment == 1 then r1, g1, b1, r2, g2, b2 = r2, g2, b2, r3, g3, b3 end
	return r1 + (r2-r1)*relperc, g1 + (g2-g1)*relperc, b1 + (b2-b1)*relperc
end

local function OnSort(x, y)
	if x and x.cpu then return x.cpu > y.cpu
	elseif x and x.mem then return x.mem > y.mem end
end

-- I forgot to include that check in first Release
-- sorry for messing up your tips ;D
local function IsOwnTip(GameTipOrQTip)
	if GameTipOrQTip and GameTipOrQTip:IsVisible() then
		if QTip and QTip:IsAcquired(L["Q_NAME"]) then return true end
		local sig = _G.GameTooltipTextRight1:GetText() or "dummy"
		if string_find(sig, L["N_TITLE"]) then return true end
	end
	return false
end

local function OnCollectTotalCPU(tbl)
	local totalcpu, totaldiff, totalmem, totalmemdiff, numactive = 0, 0, 0, 0, 0
	if tbl and QTip then
		-- if the tooltip threshold is default or low lighten the collect
		-- this gives the same results for less cpu work.
		if db.tooltipthreshold > 2 then
			for i = 1, GetNumAddOns() do
				if IsAddOnLoaded(i) then
					oldcpu[GetAddOnInfo(i)] = (scriptProfile and GetAddOnCPUUsage(i)) or nil
					oldmem[GetAddOnInfo(i)] = (db.memory and GetAddOnMemoryUsage(i)) or nil
				end
			end
		end
	end
	if scriptProfile then UpdateAddOnCPUUsage() end
	if db.memory or db.memory_ldb then UpdateAddOnMemoryUsage() end
	for i = 1, GetNumAddOns() do
		if IsAddOnLoaded(i) then
			local cpuused, cpudiff, memused, memdiff
			numactive = numactive + 1
			local addon = GetAddOnInfo(i)
			if scriptProfile then
				cpuused = GetAddOnCPUUsage(i)
				totalcpu = totalcpu + cpuused
			end
			if db.memory or db.memory_ldb then
				memused = GetAddOnMemoryUsage(i)
				totalmem = totalmem + memused
			end
			if tbl then
				if db.memory and QTip then
					memdiff = memused - (oldmem[addon] or memused)
					totalmemdiff = totalmemdiff + memdiff
					oldmem[addon] = (db.tooltipthreshold <= 2) and memused or nil
				end
				if scriptProfile and QTip then
					cpudiff = cpuused - (oldcpu[addon] or cpuused)
					totaldiff = totaldiff + cpudiff
					oldcpu[addon] = (db.tooltipthreshold <= 2) and cpuused or nil
				end
				-- using commons {} is not always good first solution
				if not mods[numactive] then mods[numactive] = {} end
				mods[numactive].add = addon
				mods[numactive].cpu = ((QTip and scriptProfile) or not (QTip or db.memory)) and cpuused or nil
				mods[numactive].mem = db.memory and memused or nil
				mods[numactive].cpudiff = (scriptProfile and QTip) and cpudiff or nil
				mods[numactive].memdiff = (db.memory and QTip) and memdiff or nil
			end
		end
	end
	if numactive and numactive ~= (lastactive or 0) then first = true end
	lastactive = numactive
	lastotalcpu = totalcpu
	lastotalmem = totalmem
	return lastotalcpu, totaldiff, lastotalmem, totalmemdiff
end

local function OnLeave(self)
	local self = self
	local GameTip = obj.onupdate or nil
	if GameTip then
		if GameTip:GetScript("OnUpdate") then GameTip:SetScript("OnUpdate", nil) end
		obj.onupdate = nil
	end
	if self and self.tooltip then
		if self.tooltip:GetScript("OnUpdate") then self.tooltip:SetScript("OnUpdate", nil) end
		QTip:Release(self.tooltip)
		self.tooltip = nil
		obj.display = nil
	end
	if not obj.OnLeave and not (scriptProfile and db.memory) then obj.OnLeave = OnLeave end
	return
end

local function OnDetachTooltip(self)
	local tip = self.tooltip
	if not db.detach then
		tip:SmartAnchorTo(self)
		if not obj.OnLeave then obj.OnLeave = OnLeave end
		if tip:GetScript("OnMouseDown") then tip:SetScript("OnMouseDown", nil) end
		if tip:GetScript("OnMouseUp") then tip:SetScript("OnMouseUp", nil) end
	else
		if not db.detacheddata then
			db.detacheddata = {}
			db.detacheddata.pt = "CENTER"
			db.detacheddata.rpt = "CENTER"
			db.detacheddata.x, db.detacheddata.y = 0, 0
		end
		tip:ClearAllPoints()
		tip:SetFrameStrata("FULLSCREEN")
		tip:EnableMouse(true)
		tip:SetResizable(true)
		tip:SetFrameLevel(1)
		tip:SetMovable(true)
		tip:SetClampedToScreen(true)
		if not ( tip:GetScript("OnMouseDown") and tip:GetScript("OnMouseUp") ) then
			tip:SetScript("OnMouseDown", function()
				paused = true
				tip:StartMoving()
			end)
			tip:SetScript("OnMouseUp", function()
				tip:StopMovingOrSizing()
				db.detacheddata.pt, _, db.detacheddata.rpt, db.detacheddata.x, db.detacheddata.y = tip:GetPoint()
				paused = false
			end)
		end
		obj.OnLeave = nil
	end
	return
end

local function recycleQTip(qtip, col)
	local num = col - #qtip.columns
	if num > 0 then
		for i = 1, num do
			qtip:AddColumn("LEFT")
		end
	else
		for i = 1 + ( #qtip.columns + num ), #qtip.columns do
			qtip:ReleaseColumn(qtip.columns[i])
			qtip.columns[i] = nil
		end
	end
end

local function DoesQTipShouldResize(qtip)
	if scriptProfile and db.memory then
		if db.icons then
			if qtip.columns and #qtip.columns ~= 11 then return recycleQTip(qtip, 11) end
		else
			if qtip.columns and #qtip.columns ~= 7 then return recycleQTip(qtip, 7) end
		end
	elseif scriptProfile or db.memory then
		if db.icons then
			if qtip.columns and #qtip.columns ~= 7 then return recycleQTip(qtip, 7) end
		else
			if qtip.columns and #qtip.columns ~= 5 then return recycleQTip(qtip, 5) end
		end
	end
end

local function DoesTheTipExpandsTooMuch(qtip)
	local parenth = (_G.UIParent:GetHeight()) or 1
	local tiph = (qtip:GetHeight() * db.scale) or 0
	if tiph > parenth then
		print(string_format("%s%s%s: %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], L["EXPANDED"]))
		db.cpukill = 5
		first = true
		return true
	end
	return false
end

local function OnQTipShow(self)
	if ( self and self.tooltip ) or not QTip then return end -- secure check
	-- Acquiring 5 columns the minimum required and then adding/deleting columns upon demands to avoid
	-- creating fantom cells and increasing QTip jobs
	local tip = QTip:Acquire(L["Q_NAME"], 5)
	obj.display = self
	self.tooltip = tip
	if not scriptProfile and not db.memory then
		local tt = tip:AddHeader()
		tip:Show()
		if db.scale and db.scale ~= tip:GetScale() then tip:SetScale(db.scale) end
		tip:SetCell(tt, 1, string_format(L["TIPHINT1"], L["COLOR_GOLD"], L["LINE_STOP"], L["COLOR_STOP"]), nil, nil, 5)
		tt = tip:AddLine()
		tip:SetCell(tt, 1, string_format(L["TIPHINT2"], L["COLOR_GOLD"], L["LINE_STOP"], L["COLOR_STOP"]), nil, nil, 5)
		tt = tip:AddLine()
		tip:SetCell(tt, 1, string_format(L["TIPHINT3"], L["COLOR_GOLD"], L["LINE_STOP"], L["COLOR_STOP"]), nil, nil, 5)
		tip:AddLine(" ")
		tt = tip:AddLine()
		tip:SetCell(tt, 1, string_format(L["TIPHINT4"], L["COLOR_HINT1"], L["COLOR_STOP"], L["COLOR_GREEN"], L["LINE_STOP"], L["COLOR_STOP"]), nil, nil, 5)
		tt = tip:AddLine()
		tip:SetCell(tt, 1, string_format(L["TIPHINT5"], L["COLOR_GREEN"], L["COLOR_STOP"], L["COLOR_HINT1"], L["COLOR_STOP"], L["COLOR_GREEN"], L["COLOR_STOP"]), nil, nil, 5)
		tip:SmartAnchorTo(self)
		return
	end
	local delay = 0
	first = true
	tip:Show()
	tip:SetScript("OnUpdate", function(_, elapsed)
		delay = delay + elapsed
		if not paused and ( delay > tonumber(db.tooltipthreshold) ) or first then
			delay = 0
			if not ( scriptProfile or db.memory ) or not IsOwnTip(tip) then OnLeave(self) return end
			local totcpu, totdiff, totmem, totmemdiff = OnCollectTotalCPU(1)
			local kill = tonumber(db.cpukill) or 0
			table_sort(mods, OnSort)
			if first then
				-- To reduce QTip's CPU usages, anything executed in this if chain happens only once,
				-- they are stable and not variable datas so re-creating them to each loops increases
				-- a lot QTip cpu usages for nothing helpful.
				if tip.lines[1] then tip:Clear() end
				DoesQTipShouldResize(tip) -- column recycling
				OnDetachTooltip(self)
				local tt
				for k in pairs(mods) do
					tt = tip:AddLine(" ")
					if tt == kill then break end
				end
				tip:AddLine(" ")
				tt = tip:AddLine()
				tip:SetCell(tt, 1, string_format("%s%s%s", L["COLOR_GOLD"], L["N_TOT"], L["COLOR_STOP"]), nil, nil, 3)
				if db.memory then
					tt = tip:AddLine()
					tip:SetCell(tt, 1, string_format("%s%s%s", L["COLOR_GOLD"], L["N_BLI"], L["COLOR_STOP"]), nil, nil, 3)
				end
				if db.framerate or db.latency then
					tip:AddLine(" ")
					if db.framerate then
						tt = tip:AddLine()
						tip:SetCell(tt, 1, string_format("%s%s%s", L["COLOR_GOLD"], L["N_FPS"], L["COLOR_STOP"]), nil, nil, 4)
					end
					if db.latency then
						tt = tip:AddLine()
						tip:SetCell(tt, 1, string_format("%s%s%s", L["COLOR_GOLD"], L["N_LAT"], L["COLOR_STOP"]), nil, nil, 4)
					end
				end
			end
			-- auto-resizing to a decent number of addons for noobs like yess ;p
			if DoesTheTipExpandsTooMuch(tip) then return end
			first = false
			if db.scale and db.scale ~= tip:GetScale() then tip:SetScale(db.scale) end
			local tt = 0
			local col = 4
			for k, v in pairs(mods) do
				tt = tt + 1
				tip:SetCell(tt, 1, string_format("%s%d%s", L["COLOR_ADDON"], tt, L["COLOR_STOP"]))
				tip:SetCell(tt, 2, " ")
				tip:SetCell(tt, 3, string_format("%s%s%s", L["COLOR_ADDON"], v.add, L["COLOR_STOP"]))
				if scriptProfile then
					-- instead of padding with a column, using a variable to avoid QTip resizes
					local str, suf = formatTime(v.cpu)
					local r, g, b = colorGradient((v.cpudiff/10)/10, 0, 1, 0, 1, 1, 0, 1, 0, 0)
					local rem = string_len(str) - 5
					local pad = string_sub("            ", 1 + rem * 2)
					if v.cpudiff == 0 then
						tip:SetCell(tt, 4, string_format("%s%s%s %s%s%s", L["COLOR_CPU"], pad, str, L["COLOR_SUFF"], suf, L["COLOR_STOP"]), nil, "RIGHT")
					else
						tip:SetCell(tt, 4, string_format("|cff%02x%02x%02x%s%s %s%s%s", r*255, g*255, b*255, pad, str, L["COLOR_SUFF"], suf, L["COLOR_STOP"]), nil, "RIGHT")
					end
					local str = string_format("%.2f", v.cpudiff/10)
					local rem = string_len(str) - 4
					local pad = string_sub("      ", 1 + rem * 2)
					tip:SetCell(tt, 5, string_format("|cff%02x%02x%02x%s%s %s%%%s", r*255, g*255, b*255, pad, str, L["COLOR_SUFF"], L["COLOR_STOP"]), nil, "RIGHT")
					if db.icons then
						tip:SetCell(tt, 6, "  ")
						tip:SetCell(tt, 7, "    ")
						local icon = (v.cpudiff == 0) and arrowc or (v.cpudiff > 0) and arrowa or (v.cpudiff < 0) and arrowb
						tip.lines[tt].cells[7]:SetBackdrop({bgFile = icon})
						if not tip.lines[tt].cells[7]:GetScript("OnHide") then
							tip.lines[tt].cells[7]:SetScript("OnHide", function(self) self:SetBackdrop(nil) self:SetScript("OnHide", nil) end)
						end
						col = 8
					else
						col = 6
					end
				end
				if db.memory then
					local str, suf = formatMemory(v.mem)
					local r, g, b = colorGradient((v.memdiff/10)/10, 0, 1, 0, 1, 1, 0, 1, 0, 0)
					local rem = string_len(str) - 5
					local pad = string_sub("          ", 1 + rem * 2)
					if v.memdiff == 0 then
						tip:SetCell(tt, col, string_format("%s%s%s %s%s%s", L["COLOR_MEM"], pad, str, L["COLOR_SUFF"], suf, L["COLOR_STOP"]), nil, "RIGHT")
					else
						tip:SetCell(tt, col, string_format("|cff%02x%02x%02x%s%s %s%s%s", r*255, g*255, b*255, pad, str, L["COLOR_SUFF"], suf, L["COLOR_STOP"]), nil, "RIGHT")
					end
					local str = string_format("%.2f", v.memdiff/10)
					local rem = string_len(str) - 4
					local pad = string_sub("      ", 1 + rem * 2)
					tip:SetCell(tt, col+1, string_format("|cff%02x%02x%02x%s%s %s%%%s", r*255, g*255, b*255, pad, str, L["COLOR_SUFF"], L["COLOR_STOP"]), nil, "RIGHT")
					if db.icons then
						tip:SetCell(tt, col+2, "  ")
						tip:SetCell(tt, col+3, "    ")
						local icon = (v.memdiff == 0) and arrowc or (v.memdiff > 0) and arrowa or (v.memdiff < 0) and arrowb
						tip.lines[tt].cells[col+3]:SetBackdrop({bgFile = icon})
						if not tip.lines[tt].cells[col+3]:GetScript("OnHide") then
							tip.lines[tt].cells[col+3]:SetScript("OnHide", function(self) self:SetBackdrop(nil) self:SetScript("OnHide", nil) end)
						end
					end
				end
				if tt == kill then break end
			end
			tt = tt + 2
			if scriptProfile then
				local totcpu, suf = formatTime(totcpu)
				tip:SetCell(tt, 4, string_format("%s%s %s%s%s", L["COLOR_GOLD"], totcpu, L["COLOR_SUFF"], suf, L["COLOR_STOP"]), nil, "RIGHT")
				local r, g, b = colorGradient((totdiff/10)/20, 0, 1, 0, 1, 1, 0, 1, 0, 0)
				tip:SetCell(tt, 5, string_format("|cff%02x%02x%02x%.2f %s%%%s", r*255, g*255, b*255, totdiff/10, L["COLOR_SUFF"], L["COLOR_STOP"]), nil, "RIGHT")
			end
			if db.memory then
				local bli = collectgarbage("count")
				local totmem, suf = formatMemory(totmem)
				local totbli, suf2 = formatMemory(bli)
				local r, g, b = colorGradient(totmem/(60*1024), 0, 1, 0, 1, 1, 0, 1, 0, 0)
				local r2, g2, b2 = colorGradient(bli/(70*1024), 0, 1, 0, 1, 1, 0, 1, 0, 0)
				local r3, g3, b3 = colorGradient((totmemdiff/10)/20, 0, 1, 0, 1, 1, 0, 1, 0, 0)
				tip:SetCell(tt, col, string_format("|cff%02x%02x%02x%s %s%s%s", r*255, g*255, b*255, totmem, L["COLOR_SUFF"], suf, L["COLOR_STOP"]), nil, "RIGHT")
				tip:SetCell(tt, col+1, string_format("|cff%02x%02x%02x%.2f %s%%%s", r3*255, g3*255, b3*255, totmemdiff/10, L["COLOR_SUFF"], L["COLOR_STOP"]), nil, "RIGHT")
				tt = tt + 1
				tip:SetCell(tt, col, string_format("|cff%02x%02x%02x%s %s%s%s", r2*255, g2*255, b2*255, totbli, L["COLOR_SUFF"], suf2, L["COLOR_STOP"]), nil, "RIGHT")
			end
			if db.framerate or db.latency then
				tt = tt + 1
				if db.framerate then
					local framerate = GetFramerate() or 0
					tt = tt + 1
					local r, g, b = colorGradient(framerate/70, 1, 0, 0, 1, 1, 0, 0, 1, 0)
					tip:SetCell(tt, db.memory and col+1 or 5, string_format("|cff%02x%02x%02x%.1f %s%s%s", r*255, g*255, b*255, framerate, L["COLOR_SUFF"], fps_, L["COLOR_STOP"]), nil, "RIGHT")
				end
				if db.latency then
					local _, _, latency = GetNetStats()
					tt = tt + 1
					if type(latency) == "number" then lastlat = latency else latency = lastlat end
					local r, g, b = colorGradient(latency/800, 0, 1, 0, 1, 1, 0, 1, 0, 0)
					tip:SetCell(tt, db.memory and col+1 or 5, string_format("|cff%02x%02x%02x%d %sms%s", r*255, g*255, b*255, latency or 0, L["COLOR_SUFF"], L["COLOR_STOP"]), nil, "RIGHT")
				end
			end
			if db.detach then
				tip:ClearAllPoints()
				tip:SetPoint(db.detacheddata.pt, nil, db.detacheddata.rpt, db.detacheddata.x, db.detacheddata.y)
			end
			tip:Show()
		end
	end)
	return
end

local function OnLDBTextUpdate(init)
	if init then
		obj.text = ""
		if tlist and #tlist > 0 then wipe(tlist) end
		return
	end
	local totcpu, totmem
	if scriptProfile or db.memory_ldb then
		if not (IsOwnTip(obj.display and obj.display.tooltip or nil) or IsOwnTip(_G.GameTooltip)) then
			totcpu, _, totmem = OnCollectTotalCPU(0) -- lighten collect, don't set to true!
		else
			totcpu = lastotalcpu
			totmem = lastotalmem
		end
	end
	if scriptProfile then
		local totcpu, suf = formatTime(totcpu)
		tlist[#tlist+1] = string_format("%s%s%s%s%s", L["COLOR_GOLD"], totcpu, L["COLOR_SUFF"], suf, L["COLOR_STOP"])
	end
	if db.memory_ldb then
		local totmem, suf = formatMemory(totmem, 1)
		local r, g, b = colorGradient(totmem/(60*1024), 0, 1, 0, 1, 1, 0, 1, 0, 0)
		tlist[#tlist+1] = string_format("|cff%02x%02x%02x%s%s%s%s", r*255, g*255, b*255, totmem, L["COLOR_SUFF"], suf, L["COLOR_STOP"])
	end
	if db.framerate_ldb then
		local fps = GetFramerate() or 0
		local r, g, b = colorGradient(fps/70, 1, 0, 0, 1, 1, 0, 0, 1, 0)
		tlist[#tlist+1] = string_format("|cff%02x%02x%02x%.1f%s%s%s", r*255, g*255, b*255, fps, L["COLOR_SUFF"], fps_, L["COLOR_STOP"])
	end
	if db.latency_ldb then
		local _, _, latency = GetNetStats()
		if type(latency) == "number" then lastlatb = latency else latency = lastlatb end
		local r, g, b = colorGradient(latency/800, 0, 1, 0, 1, 1, 0, 1, 0, 0)
		tlist[#tlist+1] = string_format("|cff%02x%02x%02x%d%sms%s", r*255, g*255, b*255, type(latency) == "number" and latency or (lastlatb or 0), L["COLOR_SUFF"], L["COLOR_STOP"])
	end
	obj.text = table_concat(tlist, " ")
	for i=1, #tlist do tlist[i] = nil end
	return
end

local function OnGameTooltipShow(GameTip)
	if obj.onupdate or not GameTip then return end -- secure check
	if not scriptProfile and not db.memory then
		GameTip:AddLine(string_format(L["TIPHINT1"], "", " ", ""), nil, nil, nil, 1)
		GameTip:AddLine(string_format(L["TIPHINT2"], "", " ", ""), nil, nil, nil, 1)
		GameTip:AddLine(string_format(L["TIPHINT3"], "", " ", ""), nil, nil, nil, 1)
		GameTip:AddLine(" ")
		GameTip:AddLine(string_format(L["TIPHINT4"], L["COLOR_HINT1"], L["COLOR_STOP"], "", " ", ""), 0.2, 1, 0.2, 1)
		GameTip:AddLine(string_format(L["TIPHINT5"], "", "", L["COLOR_HINT1"], L["COLOR_STOP"], "", ""), 0.2, 1, 0.2, 1)
		GameTip:Show()
		return
	end
	GameTip:AddLine(" ")
	local delay, first = 0, true
	obj.onupdate = CreateFrame("Frame")
	obj.onupdate:SetScript("OnUpdate", function(_, elapsed)
		delay = delay + elapsed
		if ( delay > tonumber(db.tooltipthreshold) ) or first then
			delay = 0
			if not ( scriptProfile or db.memory ) or not ( IsOwnTip(GameTip) or first ) then OnLeave() return end -- secure check
			first = false
			GameTip:ClearLines()
			GameTip:AddDoubleLine(" ", L["N_TITLE"]) -- secure check
			GameTip:AddDoubleLine(" ", db.memory and string_format("[%s]", L["N_MEM"]) or "[CPU]")
			GameTip:AddLine(" ")
			local totcpu, _, totmem = OnCollectTotalCPU(1)
			local kill = tonumber(db.cpukill) or 0
			table_sort(mods, OnSort)
			for k, v in pairs(mods) do
				if db.memory then
					local str, suf = formatMemory(v.mem)
					local r, g, b = colorGradient(v.mem/(40*1024), 0, 1, 0, 1, 1, 0, 1, 0, 0)
					GameTip:AddDoubleLine(string_format("%d. %s", k, v.add), string_format("%s %s%s%s", str, L["COLOR_SUFF"], suf, L["COLOR_STOP"]), 0, 1, 1, r, g, b)
				elseif scriptProfile then
					local str, suf = formatTime(v.cpu)
					GameTip:AddDoubleLine(string_format("%d. %s", k, v.add), string_format("%s %s%s%s", str, L["COLOR_SUFF"], suf, L["COLOR_STOP"]), 0, 1, 1, 0, 1, 0)
				end
				if k and k == kill then break end
			end
			if db.memory then
				local str, suf = formatMemory(totmem)
				local r, g, b = colorGradient(totmem/(60*1024), 0, 1, 0, 1, 1, 0, 1, 0, 0)
				GameTip:AddLine(" ")
				GameTip:AddDoubleLine(string_format("%s%s%s", L["COLOR_GOLD"], L["N_TOT"], L["COLOR_STOP"]), string_format("|cff%02x%02x%02x%s %s%s%s", r*255, g*255, b*255, str, L["COLOR_SUFF"], suf, L["COLOR_STOP"]), 1, 1, 1, 1, 1, 1)
			elseif scriptProfile then
				local data, suf = formatTime(totcpu)
				GameTip:AddLine(" ")
				GameTip:AddDoubleLine(string_format("%s%s%s", L["COLOR_GOLD"], L["N_TOT"], L["COLOR_STOP"]), string_format("%s%s %s%s%s", L["COLOR_GOLD"], data, L["COLOR_SUFF"], suf, L["COLOR_STOP"]), 1, 1, 1, 1, 1, 1)
			end
			if db.memory then
				local bli = collectgarbage("count")
				local totbli, suf = formatMemory(bli)
				local r, g, b = colorGradient(bli/(70*1024), 0, 1, 0, 1, 1, 0, 1, 0, 0)
				GameTip:AddDoubleLine(string_format("%s%s%s", L["COLOR_GOLD"], L["N_BLI"], L["COLOR_STOP"]), string_format("%s %s%s%s", totbli, L["COLOR_SUFF"], suf, L["COLOR_STOP"]), nil, nil, nil, r, g, b)
			end
			if db.framerate or db.latency then
				GameTip:AddLine(" ")
				if db.framerate then
					local fps = GetFramerate() or 0
					local r, g, b = colorGradient(fps/70, 1, 0, 0, 1, 1, 0, 0, 1, 0)
					GameTip:AddDoubleLine(string_format("%s%s%s", L["COLOR_GOLD"], L["N_FPS"], L["COLOR_STOP"]), string_format("%.1f %s%s%s", fps, L["COLOR_SUFF"], fps_, L["COLOR_STOP"]), nil, nil, nil, r, g, b)
				end
				if db.latency then
					local _, _, latency = GetNetStats()
					if type(latency) == "number" then lastlat = latency else latency = lastlat end
					local r, g, b = colorGradient(latency/800, 0, 1, 0, 1, 1, 0, 1, 0, 0)
					GameTip:AddDoubleLine(string_format("%s%s%s", L["COLOR_GOLD"], L["N_LAT"], L["COLOR_STOP"]), string_format("%d %sms%s", latency or 0, L["COLOR_SUFF"], L["COLOR_STOP"]), nil, nil, nil, r, g, b)
				end
			end
			GameTip:Show()
		end
	end)
	return
end

local function OnToggleLDBText()
	if obj.onupdate_b then obj.onupdate_b:SetScript("OnUpdate", nil) end
	obj.onupdate_b = CreateFrame("Frame")
	if scriptProfile or db.memory_ldb or db.latency_ldb or db.framerate_ldb then
		local first, delay = true, 0
		obj.onupdate_b:SetScript("OnUpdate", function(_, elapsed)
			delay = delay + elapsed
			if delay > tonumber(db.ldbtextthreshold) or ( delay > 3 and first ) then
				delay, first = 0, false
				OnLDBTextUpdate(nil)
			end
		end)
	else
		if obj.onupdate_b:GetScript("OnUpdate") then obj.onupdate_b:SetScript("OnUpdate", nil) end
		obj.onupdate_b = nil
		OnLDBTextUpdate(1)
	end
	return
end

local function OnCheckStatus(n)
	if n == "cpukill" then return string_format("[%s%d%s]", L["COLOR_GREEN"], db.cpukill, L["COLOR_STOP"])
	elseif n == "ldbtextthreshold" then return string_format("[%s%d%s]", L["COLOR_GREEN"], db.ldbtextthreshold, L["COLOR_STOP"])
	elseif n == "tooltipthreshold" then return string_format("[%s%.1f%s]", L["COLOR_GREEN"], db.tooltipthreshold, L["COLOR_STOP"])
	elseif n == "scale" then return string_format("[%s%.2f%s]", L["COLOR_GREEN"], db.scale, L["COLOR_STOP"])
	end
	return string_format(n and "[%sON%s]" or "[%sOFF%s]", n and L["COLOR_GREEN"] or L["COLOR_RED"], L["COLOR_STOP"])
end

local function OnToggleMemory(ldb)
	if ldb ~= 13 then
		first = true
		db.memory = not db.memory
	else
		db.memory_ldb = not db.memory_ldb
	end
	OnToggleLDBText()
	print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], (ldb ~= 13) and OnCheckStatus(db.memory) or OnCheckStatus(db.memory_ldb), string_format("%s %s", L["N_MEM"], (ldb ~= 13) and L["N_DAT_TIP"] or L["N_DAT_LDB"])))
	if not db.memory then
		if oldmem and #oldmem > 0 then wipe(oldmem) end
	end
	if not ( scriptProfile or db.memory or db.memory_ldb ) then
		if mods and #mods > 0 then wipe(mods) end
	end
	return
end

local function OnToggleMinimap(n)
	if n ~= 13 then db.minimap = not db.minimap end
	if db.minimap then
		minimap = LibStub("LibDBIcon-1.0", true)
		if minimap then
			if not db.minimapdata then db.minimapdata = {} end
			if not db.minimapdata.minimapPos then db.minimapdata.minimapPos = random(0, 360) end
			if not minimap:IsRegistered(L["N_TITLE"]) then minimap:Register(L["N_TITLE"], obj, db.minimapdata) end
			minimap:Show(L["N_TITLE"])
		else
			if n ~= 13 then print(string_format("%s%s%s: %s%s %s%s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], L["COLOR_GREY"], L["N_MAP"], L["NOT_DBICO"], L["COLOR_STOP"])) end
			return
		end
	else
		if minimap and minimap:IsRegistered(L["N_TITLE"]) then minimap:Hide(L["N_TITLE"]) end
		minimap = nil
	end
	if n ~= 13 then print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus(db.minimap), L["N_MAP"])) end
	return
end

local function OnToggleQTip(n)
	if n ~= 13 then
		db.qtip = not db.qtip
		first = true
	end
	if db.qtip then
		QTip = LibStub("LibQTip-1.0", true) or nil
		if QTip then
			local GameTip = _G.GameTooltip
			if GameTip and GameTip:IsVisible() then
				if IsOwnTip(GameTip) then GameTip:Hide() OnLeave() end
			end
			obj.OnEnter = OnQTipShow
			obj.OnTooltipShow = nil
		else
			obj.OnEnter = nil
			obj.OnTooltipShow = OnGameTooltipShow
			if n ~= 13 then print(string_format("%s%s%s: %s%s %s%s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], L["COLOR_GREY"], L["N_L_QTIP"], L["NOT_QTIP"], L["COLOR_STOP"])) end
			return
		end
	else
		if obj and obj.display then OnLeave(obj.display) end
		QTip = nil
		obj.OnEnter = nil
		obj.OnTooltipShow = OnGameTooltipShow
	end
	if n ~= 13 then print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus(db.qtip), L["N_L_QTIP"])) end
	if not db.qtip then
		if oldmem and #oldmem > 0 then wipe(oldmem) end
		if oldcpu and #oldcpu > 0 then wipe(oldcpu) end
	end
	return
end

local function OnSetMaxAddOns(num)
	first = true
	if tonumber(num) then
		db.cpukill = tonumber(num)
		print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus("cpukill"), L["N_MOD"]))
	end
	return
end

local function OnSetScale(value, antiflood)
	db.scale = value
	if antiflood ~= 13 then print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus("scale"), L["N_SC"])) end
	return
end

local function OnToggleIcons()
	first = true
	db.icons = not db.icons
	if db.icons and not QTip then
		print(string_format("%s%s%s: %s%s %s%s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], L["COLOR_GREY"], L["N_ICO"], L["NOT_QTIP"], L["COLOR_STOP"]))
		return
	end
	print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus(db.icons), L["N_ICO"]))
	return
end

local function OnToggleDewdrop(n)
	if n ~= 13 then db.dewdrop = not db.dewdrop end
	if db.dewdrop then
		dewdrop = LibStub("Dewdrop-2.0", true)
		dewoptions = {
			type = "group", name = L["N_TITLE"], desc = L["N_TITLE"],
			args = {
				datas_tooltips = { type = "group", name = L["N_DAT_TIP"], desc = L["N_DAT_TIP"], order = 5,
					args = {
					memory = { type = "toggle", name = L["N_MEM"], desc = string_format(L["TOGGLE_MEM"], L["N_DAT_TIP"]),
						get = function() return db.memory end,
						set = OnToggleMemory , order = 1 },
					fps = { type = "toggle", name = L["N_FPS"], desc = string_format(L["TOGGLE_FPS"], L["N_DAT_TIP"]),
						get = function() return db.framerate end,
						set = function()
							first = true
							db.framerate = not db.framerate
							print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus(db.framerate), string_format("%s %s", L["N_FPS"], L["N_DAT_TIP"])))
						end, order = 2 },
					lat = { type = "toggle", name = L["N_LAT"], desc = string_format(L["TOGGLE_LAT"], L["N_DAT_TIP"]),
						get = function() return db.latency end,
						set = function()
							first = true
							db.latency = not db.latency
							print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus(db.latency), string_format("%s %s", L["N_LAT"], L["N_DAT_TIP"])))
						end, order = 3 }, } },
				datas_ldb = { type = "group", name = L["N_DAT_LDB"], desc = L["N_DAT_LDB"], order = 6,
					args = {
					memory = { type = "toggle", name = L["N_MEM"], desc = string_format(L["TOGGLE_MEM"], L["N_DAT_LDB"]),
						get = function() return db.memory_ldb end,
						set = function() OnToggleMemory(13) end, order = 1 },
					fps = { type = "toggle", name = L["N_FPS"], desc = string_format(L["TOGGLE_FPS"], L["N_DAT_LDB"]),
						get = function() return db.framerate_ldb end,
						set = function()
							db.framerate_ldb = not db.framerate_ldb
							OnToggleLDBText()
							print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus(db.framerate_ldb), string_format("%s %s", L["N_FPS"], L["N_DAT_LDB"])))
						end, order = 2 },
					lat = { type = "toggle", name = L["N_LAT"], desc = string_format(L["TOGGLE_LAT"], L["N_DAT_LDB"]),
						get = function() return db.latency_ldb end,
						set = function()
							db.latency_ldb = not db.latency_ldb
							OnToggleLDBText()
							print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus(db.latency_ldb), string_format("%s %s", L["N_LAT"], L["N_DAT_LDB"])))
						end, order = 3 }, } },
				minimap = { type = "toggle", name = L["N_MAP"], desc = string_format("%s %s", L["TOGGLE_MAP"], L["NOT_DBICO"]),
					get = function() return db.minimap end,
					set = OnToggleMinimap, order = 8 },
				icons = { type = "toggle", name = L["N_ICO"], desc = string_format("%s %s", L["TOGGLE_ICO"], L["NOT_QTIP"]),
					get = function() return db.icons end,
					set = OnToggleIcons, order = 9 },
				cpukill = { type = "text", name = L["N_MOD"], desc = L["TOGGLE_KLL"], usage = "<Number>",
					get = function() return tonumber(db.cpukill) end,
					set = OnSetMaxAddOns, order = 10 },
				scale = { type = "range", name = L["N_SC"], desc = string_format("%s %s", L["TOGGLE_SC"], L["NOT_QTIP"]), min = .1, max = 2, step = .01,
					get = function() return tonumber(db.scale) end,
					set = function(value) OnSetScale(value, 13) end, order = 11 },
				thresholds = { type = "group", name = L["N_THR"], desc = L["N_THR"], order = 12,
					args = {
						ldbtextthreshold = { type = "range", name = L["N_THR_LDB"], desc = L["TOGGLE_LDB"], min = 1, max = 120, step = 1,
							get = function() return tonumber(db.ldbtextthreshold) end,
							set = function(value) db.ldbtextthreshold = value end, order = 1 },
						tooltipthreshold = { type = "range", name = L["N_THR_TIP"], desc = L["TOGGLE_TIP"], min = .1, max = 5, step = .1,
							get = function() return tonumber(db.tooltipthreshold) end,
							set = function(value) db.tooltipthreshold = value end, order = 2 }, } },
				libraries = { type = "group", name = L["N_LIB"], desc = L["N_LIB"], order = 13,
					args = {
						LibQTip = { type = "toggle", name = L["N_L_QTIP"], desc = L["TOGGLE_L_QTIP"],
							get = function() return db.qtip end,
							set = OnToggleQTip, order = 1 },
						LibDBIcon = { type = "toggle", name = L["N_L_ICO"], desc = L["TOGGLE_L_ICO"],
							get = function() return db.minimap end,
							set = OnToggleMinimap, order = 2 },
						DewdropLib = { type = "toggle", name = L["N_L_DEW"], desc = L["TOGGLE_L_DEW"],
							get = function() return db.dewdrop end,
							set = OnToggleDewdrop, order = 3 }, } },
				detach = { type = "toggle", name = L["N_DET"], desc = string_format("%s %s", L["TOGGLE_DET"], L["NOT_QTIP"]),
					get = function() return db.detach end,
					set = function()
						db.detach = not db.detach
						first = true
						if db.detach and not QTip then
							print(string_format("%s%s%s: %s%s %s%s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], L["COLOR_GREY"], L["N_DET"], L["NOT_QTIP"], L["COLOR_STOP"]))
							return
						end
						print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus(db.detach), L["N_DET"]))
					end, order = 14 },
				separatorb = { type = "header", name = " ", order = 15 },
				separatorc = { type = "header", name = " ", order = 16 },
				garbage = { type = "execute", name = L["N_GAR"], desc = L["TOGGLE_GAR"],
					func = function()
						collectgarbage("collect")
						print(string_format("%s%s%s: %s collect", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], L["N_GAR"]))
					end, order = 29 },
				hide = { type = "execute", name = L["N_HID"], desc = string_format("%s %s", L["TOGGLE_HID"], L["NOT_QTIP"]),
					func = function()
						if db.detach and ( obj and obj.display ) then
							OnLeave(obj.display)
						end
					end, order = 28 },
				cpuprof = { type = "toggle", name = L["N_CPU"], desc = L["TOGGLE_CPU"],
					get = function() return scriptProfile end,
					set = function()
						if scriptProfile then SetCVar("scriptProfile", 0)
						else SetCVar("scriptProfile", 1) end
						return ReloadUI()
					end, order = 30 },
				header = { type = "header", name = string_format("%s r|cff7fff7f%s|r by |cffabd473Merah|r", L["N_TITLE"], MINOR_VERSION), order = 1 },
				header2 = { type = "header", name = "La Croisade \195\137carlate", order = 2 },
				separatora = { type = "header", name = " ", order = 3 },
			}
		}
		if not dewdrop then
			if n ~= 13 then print(string_format("%s%s%s: %s%s %s%s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], L["COLOR_GREY"], L["N_L_DEW"], L["NOT_DEW"], L["COLOR_STOP"])) end
			if type(dewoptions) == "table" then wipe(dewoptions) end
			dewoptions = nil
			return
		end
	else
		if dewdrop then dewdrop:Close(1) end
		if type(dewoptions) == "table" then wipe(dewoptions) end
		dewdrop = nil
		dewoptions = nil
	end
	if n ~= 13 then print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus(db.dewdrop), L["N_L_DEW"])) end
	return
end

local function OnClick(self, button)
	if button == "LeftButton" then
		if IsControlKeyDown() and IsAltKeyDown() then
			if scriptProfile then SetCVar("scriptProfile", 0)
			else SetCVar("scriptProfile", 1) end
			return ReloadUI()
		elseif IsShiftKeyDown() then
			if db.detach and ( obj and obj.display ) then
				OnLeave(obj.display)
			end
			return
		elseif IsControlKeyDown() then
			collectgarbage("collect")
			print(string_format("%s%s%s: %s collect", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], L["N_GAR"]))
			return
		end
	end
	if db.dewdrop and dewdrop then
		dewdrop:Open(self, "children", function() dewdrop:FeedAceOptionsTable(dewoptions) end)
		local dew = _G.Dewdrop20Level1
		if dew and self then
			local point = dew:GetPoint() or "none"
			if point == "TOPRIGHT" then
				dew:ClearAllPoints()
				dew:SetFrameStrata("TOOLTIP")
				dew:SetPoint("TOPRIGHT", self, "BOTTOMLEFT")
			elseif point == "TOPLEFT" then
				dew:ClearAllPoints()
				dew:SetFrameStrata("TOOLTIP")
				dew:SetPoint("TOPLEFT", self, "BOTTOMRIGHT")
			elseif point == "BOTTOMRIGHT" then
				dew:ClearAllPoints()
				dew:SetFrameStrata("TOOLTIP")
				dew:SetPoint("BOTTOMRIGHT", self, "TOPLEFT")
			elseif point == "BOTTOMLEFT" then
				dew:ClearAllPoints()
				dew:SetFrameStrata("TOOLTIP")
				dew:SetPoint("BOTTOMLEFT", self, "TOPRIGHT")
			end
		end
	end
	return
end

local function OnSlash(k)
	if type(k) == "string" then
		local opt = string_sub(k, 1, 5) or nil
		local opt_ = string_sub(k, 1, 6) or nil
		if k == "mini" or k == string_lower(L["N_MAP"]) then
			OnToggleMinimap()
			return
		elseif k == "mem" or k == string_lower(L["N_MEM"]) then
			OnToggleMemory()
			return
		elseif k == "mem2" then
			OnToggleMemory(13)
			return
		elseif k == "fps" or k == string_lower(L["N_FPS"]) then
			first = true
			db.framerate = not db.framerate
			print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus(db.framerate), string_format("%s %s", L["N_FPS"], L["N_DAT_TIP"])))
			return
		elseif k == "fps2" then
			db.framerate_ldb = not db.framerate_ldb
			OnToggleLDBText()
			print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus(db.framerate_ldb), string_format("%s %s", L["N_FPS"], L["N_DAT_LDB"])))
			return
		elseif k == "lat" or k == string_lower(L["N_LAT"]) then
			first = true
			db.latency = not db.latency
			print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus(db.latency), string_format("%s %s", L["N_LAT"], L["N_DAT_TIP"])))
			return
		elseif k == "lat2" then
			db.latency_ldb = not db.latency_ldb
			OnToggleLDBText()
			print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus(db.latency_ldb), string_format("%s %s", L["N_LAT"], L["N_DAT_LDB"])))
			return
		elseif k == "prof" or k == "profiling" then
			if scriptProfile then SetCVar("scriptProfile", 0)
			else SetCVar("scriptProfile", 1) end
			return ReloadUI()
		elseif k == "ico" or k == string_lower(L["N_ICO"]) then
			OnToggleIcons()
			return
		elseif k == "qtip" or k == string_lower(L["N_L_QTIP"]) then
			OnToggleQTip()
			return
		elseif k == "dbico" or k == string_lower(L["N_L_ICO"]) then
			OnToggleMinimap()
			return
		elseif k == "dew" or k == string_lower(L["N_L_DEW"]) then
			OnToggleDewdrop()
			return
		elseif opt == "kill " then
			local num = string_gsub(k, opt, "")
			if tonumber(num) then
				OnSetMaxAddOns(tonumber(num))
				return
			end
		elseif opt == "Tldb " then
			local num = string_gsub(k, opt, "")
			num = tonumber(num)
			if num then
				db.ldbtextthreshold = num
				print(string_format("%s%s%s: %s %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus("ldbtextthreshold"), L["N_THR_LDB"], L["N_thr"]))
				return
			end
		elseif opt == "Ttip " then
			local num = string_gsub(k, opt, "")
			num = tonumber(num)
			if num then
				db.tooltipthreshold = num
				print(string_format("%s%s%s: %s %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus("tooltipthreshold"), L["N_THR_TIP"], L["N_thr"]))
				return
			end
		elseif opt_ == "scale " then
			local num = string_gsub(k, opt, "")
			if tonumber(num) then
				OnSetScale(tonumber(num))
				return
			end
		elseif k == string_lower(L["N_DET"]) or k == "detached" then
			db.detach = not db.detach
			first = true
			if db.detach and not QTip then
				print(string_format("%s%s%s: %s%s %s%s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], L["COLOR_GREY"], L["N_DET"], L["NOT_QTIP"], L["COLOR_STOP"]))
				return
			end
			print(string_format("%s%s%s: %s %s", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], OnCheckStatus(db.detach), L["N_DET"]))
			return
		elseif k == "garb" then
			collectgarbage("collect")
			print(string_format("%s%s%s: %s collect", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"], L["N_GAR"]))
			return
		elseif k == string_lower(L["N_HID"]) then
			if db.detach and ( obj and obj.display ) then
				OnLeave(obj.display)
			end
			return
		elseif k == "reset" then
			for k in pairs(db) do
				db[k] = nil
			end
			db.minimap = false
			db.memory = true
			db.memory_ldb = true
			db.icons = false
			db.framerate = true
			db.framerate_ldb = true
			db.latency = true
			db.latency_ldb = true
			db.detach = false
			db.qtip = true
			db.dewdrop = true
			db.cpukill = 0
			db.scale = 1
			db.ldbtextthreshold = 10
			db.tooltipthreshold = 1
			SetCVar("scriptProfile", 0)
			ReloadUI()
			return
		end
	end
	print(string_format("%s%s%s:", L["COLOR_ACE3"], L["N_TITLE"], L["COLOR_STOP"]))
	print(OnCheckStatus(scriptProfile), string_format("%s%s/cpu prof%s%s%s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], L["TOGGLE_CPU"]))
	print(OnCheckStatus(db.memory), string_format("%s%s/cpu mem%s%s%s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], string_format(L["TOGGLE_MEM"], L["N_DAT_TIP"])))
	print(OnCheckStatus(db.memory_ldb), string_format("%s%s/cpu mem2%s%s%s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], string_format(L["TOGGLE_MEM"], L["N_DAT_LDB"])))
	print(OnCheckStatus(db.framerate), string_format("%s%s/cpu fps%s%s%s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], string_format(L["TOGGLE_FPS"], L["N_DAT_TIP"])))
	print(OnCheckStatus(db.framerate_ldb), string_format("%s%s/cpu fps2%s%s%s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], string_format(L["TOGGLE_FPS"], L["N_DAT_LDB"])))
	print(OnCheckStatus(db.latency), string_format("%s%s/cpu lat%s%s%s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], string_format(L["TOGGLE_LAT"], L["N_DAT_TIP"])))
	print(OnCheckStatus(db.latency_ldb), string_format("%s%s/cpu lat2%s%s%s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], string_format(L["TOGGLE_LAT"], L["N_DAT_LDB"])))
	print(OnCheckStatus(db.minimap), string_format("%s%s/cpu mini%s%s%s %s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], L["TOGGLE_MAP"], L["NOT_DBICO"]))
	print(OnCheckStatus(db.icons), string_format("%s%s/cpu ico%s%s%s %s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], L["TOGGLE_ICO"], L["NOT_QTIP"]))
	print(OnCheckStatus(db.detach), string_format("%s%s/cpu detach%s%s%s %s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], L["TOGGLE_DET"], L["NOT_QTIP"]))
	print(OnCheckStatus(db.qtip), string_format("%s%s/cpu qtip%s%s%s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], L["TOGGLE_L_QTIP"]))
	print(OnCheckStatus(db.minimap), string_format("%s%s/cpu dbico%s%s%s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], L["TOGGLE_L_ICO"]))
	print(OnCheckStatus(db.dewdrop), string_format("%s%s/cpu dew%s%s%s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], L["TOGGLE_L_DEW"]))
	print(OnCheckStatus("cpukill"), string_format("%s%s/cpu kill <number>%s%s%s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], L["TOGGLE_KLL"]))
	print(OnCheckStatus("ldbtextthreshold"), string_format("%s%s/cpu Tldb <number>%s%s%s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], L["TOGGLE_LDB"]))
	print(OnCheckStatus("tooltipthreshold"), string_format("%s%s/cpu Ttip <number>%s%s%s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], L["TOGGLE_TIP"]))
	print(OnCheckStatus("scale"), string_format("%s%s/cpu scale <number>%s%s%s %s", L["SLASH_SEP"], L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], L["TOGGLE_SC"], L["NOT_QTIP"]))
	print(string_format("%s/cpu garb%s%s%s", L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], L["TOGGLE_GAR"]))
	print(string_format("%s/cpu hide%s%s%s", L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], L["TOGGLE_HID"]))
	print(string_format("%s/cpu reset%s%s%s", L["COLOR_GOLD"], L["COLOR_STOP"], L["SLASH_SEP"], L["TOGGLE_RST"]))
	return
end

local function OnEvent(_, _, addon)
	if addon == "Broker_CPU" then
		if event then
			event:UnregisterEvent("ADDON_LOADED")
			event:SetScript("OnEvent", nil)
			event = nil
		end
		local CPU = LibStub("LibDataBroker-1.1")
		scriptProfile = GetCVar("scriptProfile") == "1"
		BROKERCPUDB = _G.BROKERCPUDB or {}
		db = BROKERCPUDB
		if type(db.minimap) ~= "boolean" then db.minimap = false end
		if type(db.memory) ~= "boolean" then db.memory = true end
		if type(db.memory_ldb) ~= "boolean" then db.memory_ldb = true end
		if type(db.icons) ~= "boolean" then db.icons = false end
		if type(db.framerate) ~= "boolean" then db.framerate = true end
		if type(db.framerate_ldb) ~= "boolean" then db.framerate_ldb = true end
		if type(db.latency) ~= "boolean" then db.latency = true end
		if type(db.latency_ldb) ~= "boolean" then db.latency_ldb = true end
		if type(db.detach) ~= "boolean" then db.detach = false end
		if type(db.qtip) ~= "boolean" then db.qtip = true end
		if type(db.dewdrop) ~= "boolean" then db.dewdrop = true end
		if type(db.cpukill) ~= "number" then db.cpukill = 0 end
		if type(db.scale) ~= "number" then db.scale = 1 end
		if type(db.ldbtextthreshold) ~= "number" then db.ldbtextthreshold = 10 end
		if type(db.tooltipthreshold) ~= "number" then db.tooltipthreshold = 1 end
		OnInitializeLocalization()
		_G.SLASH_BROKERCPU1 = "/broker_cpu"
		_G.SLASH_BROKERCPU2 = "/brokercpu"
		_G.SLASH_BROKERCPU3 = "/cpu"
		_G.SlashCmdList.BROKERCPU = OnSlash
		obj = CPU:NewDataObject(L["N_TITLE"], {
			type = "data source",
			text = "",
			icon = icon,
			OnClick = OnClick,
			OnLeave = OnLeave,
		})
		OnToggleLDBText()
		OnToggleQTip(13)
		OnToggleMinimap(13)
		OnToggleDewdrop(13)
	end
	return
end

event = CreateFrame("Frame")
event:SetScript("OnEvent", OnEvent)
event:RegisterEvent("ADDON_LOADED")
